﻿// Inner Fire 游戏引擎库
// MESH的创建器
//
// Copyright (c) 2024 by 尔西大帝. All rights reserved.
//
// 用于生成简单的模型。
// 根据《DirectX12游戏开发实战》中的工具类修改。原作者：Frank Luna
//
// Author: 尔西大帝
// Email: 2670613871@qq.com
// Created: 2024-11-19

#pragma once

#include <dx/core/dx_core.h>
#include <dx/mesh/dx_mesh.h>
#include <EASTL/vector.h>
#include <xml_reader.h>

namespace ifire::dx {

enum class GeometryType : int {
  Box = 0,
  Cylinder,
  Sphere,
  Geosphere,
  Grid,
  Quad,
  Count
};

class GeometryGenerator {
public:
  using uint16 = std::uint16_t;
  using uint32 = std::uint32_t;

  struct Vertex {
    XMFLOAT3 Position;
    XMFLOAT3 Normal;
    XMFLOAT3 TangentU;
    XMFLOAT2 TexC;

    Vertex() {}
    Vertex(const XMFLOAT3& _position, const XMFLOAT3& _normal,
        const XMFLOAT3& _tangent, const XMFLOAT2& _texcoord)
        : Position(_position)
        , Normal(_normal)
        , TangentU(_tangent)
        , TexC(_texcoord) {}
    Vertex(float px, float py, float pz, // Position
        float nx, float ny, float nz,    // Normal
        float tx, float ty, float tz,    // Tangent
        float u, float v)                // Texcoord
        : Position(px, py, pz)
        , Normal(nx, ny, nz)
        , TangentU(tx, ty, tz)
        , TexC(u, v) {}
  };

  struct MeshData {
    eastl::vector<Vertex> Vertices;
    eastl::vector<uint32> Indices32;

    eastl::vector<uint16>& GetIndices16() {
      if (indices16.empty()) {
        indices16.resize(Indices32.size());
        for (size_t i = 0; i < Indices32.size(); i++) {
          indices16[i] = static_cast<uint16>(Indices32[i]);
        }
      }
      return indices16;
    }

    void ComputeNormal();

  private:
    eastl::vector<uint16> indices16;
  };

  using MeshDataPtr = std::unique_ptr<MeshData>;

  static void ComputeAvgNormal(
      eastl::vector<Vertex>& vertices, const eastl::vector<uint32>& indices);
  // 生成柱状体
  MeshDataPtr CreateCylinder(float bottom_radius, float top_radius,
      float height, uint32 slice_count, uint32 stack_count);
  // 生成柱状体的顶部
  void BuildCylinderTopCap(float bottom_radius, float top_radius, float height,
      uint32 slice_count, uint32 stack_count, MeshData& mesh);
  // 生成柱状体的底部
  void BuildCylinderBottomCap(float bottom_radius, float top_radius,
      float height, uint32 slice_count, uint32 stack_count, MeshData& mesh);

  // 生成球体网格
  MeshDataPtr CreateSphere(
      float radius, uint32 slice_count, uint32 stack_count);

  // 生成几何球体
  MeshDataPtr CreateGeoshpere(float radius, uint32 sub_divisions);
  // 生成一个立方体
  MeshDataPtr CreateBox(
      float width, float height, float depth, uint32 sub_divisions);
  // 生成一个网格
  MeshDataPtr CreateGrid(float width, float depth, uint32 m, uint32 n);
  // 生成一个面片
  MeshDataPtr CreateQuad(
      float x, float y, float width, float height, float depth);

private:
  void Subdivide(MeshData& mesh);
  // 求得两个顶点的中点
  Vertex MidPoint(const Vertex& v0, const Vertex& v1);
};

struct GeometrySetting {
  GeometrySetting() = default;
  GeometrySetting(const ikit::XmlNode& node, int index);
  std::unique_ptr<GeometryGenerator::MeshData> Create(GeometryGenerator& gen);

  int index;
  GeometryType type;
  ikit::sstr name;
  // 每个不同的Geo取不同的项目，并不冲突
  float x;
  float y;
  float width;
  float height;
  float radius;
  float top_radius;
  float bottom_radius;
  int sub_divisions;
  float depth;
  uint32_t row;
  uint32_t column;
  uint32_t slice_count;
  uint32_t stack_count;
};

std::unique_ptr<DxMesh> LoadGeometries(const std::string_view& filename,
    ID3D12Device* device, ID3D12GraphicsCommandList* cmd_list);
} // namespace ifire